<?php

class Application
{
	private $request;
	private $response;
	
	public function __construct()
	{
		session_start();
		
		// Do all ini sets
		$inisets = ConfigLoader::get_all_values('iniset');
		if (is_array($inisets))
		{
			foreach ($inisets as $ini_key => $value)
			{
				ini_set($ini_key, $value);
			}
		}
	}
	
	public function begin_request()
	{
		$args = array();
		
		// Always allow GET vars (even when in a POST request)
		$args['get'] = $this->clean_submitted($_GET);
		
		// Only allow POST data if this is a POST REQUEST
		if ($_SERVER['REQUEST_METHOD'] == 'POST') {
			$args['post'] = $this->clean_submitted($_POST);
		}
		
		$args['session'] = $this->clean_submitted($_SESSION);
		$args['cookies'] = $this->clean_submitted($_COOKIE);
		$args['files'] = $this->clean_submitted($_FILES);
		
		// HTTP
		$args['http'] = array(
			'method' => $_SERVER['REQUEST_METHOD'],
			'host' => $_SERVER['HTTP_HOST'] ?: '',
			'user_agent' => $_SERVER['USER_AGENT'] ?: '',
			'remote_ip' => $_SERVER['REMOTE_ADDR'] ?: ''
		);
		
		// META
		$args['meta'] = array(
			'request_start' => $_SERVER['REQUEST_TIME'],
			'query_string' => $_SERVER['QUERY_STRING'],
			'secure' => $_SERVER['HTTPS']
		);
		
		// Remove access to superglobals; only data provided in the request
		// object may be available
		unset($_GET);
		unset($_POST);
		unset($_SESSION);
		unset($_COOKIE);
		unset($_FILE);
		unset($_SERVER);
		
		// Instantiate request (URI does not include GET params)
		$this->request = new Request($_SERVER['SCRIPT_NAME'],
 			$args['meta']['query_string'], $args);
		
		// Process filters
		/*
		$filters = ConfigLoader::get_simple_value('general', 'filters');
		
		foreach ($filters as $filter)
		{
			$filter_resp = $filter::process_incoming_request($this->request)
			if ($filter_resp instanceof Response)
			{
				$this->send_response($filter_resp);
			}
		}
		*/
	}
	
	public function write_response()
	{
		// Start router
		$router = new URIRouter();
		
		// Get router file
		$route_loc = ConfigLoader::get_simple_value('general', 'routes_file');
		$route_file = 'app/'.$route_loc.'.php';
		
		// This will result in a fatal error if the file does not exist, as it
		// should.  After this, the routing table should still be available as
		// ``$router`` (initialized above).
		require_once($route_file);
		
		// Load in contrib routers
		;
		
		// Create default index route to fall back to, if not one already.
		if (is_null($router->has_patterns()))
		{
			$router->connect(new URIPattern(array(
				'name' => 'mylib_default_index',
				'controller' => 'Default',
				'view' => 'index',
				'uri' => '^/$'
			)));
		}
		
		// Match uri
		try {
			// This may raise an Http404Exception if it does not find a URI.
			$handle = $router->match_address($this->request->getURI());
			$response = $this->render_page_call($handle['controller'],
				$handle['view'], $handle['args']);
			$this->send_response($response);
		} catch (Exception $exception) {
			// Nicely print the error message
			$this->send_exception($exception);
		}
	}
	
	private function send_response(Response $response) {
		foreach ($response->get_headers() as $key => $value)
		{
			header($key.': '.$value);
		}
		
		echo $response->get_body();
		exit();
	}
	
	private function send_exception(Exception $exception) {
		$msg = '<h1>Exception: '.get_class($exception).'</h1><h2>Line '.
			$exception->getLine().' of '.$exception->getFile().
			'</h2><p>'.$exception->getMessage().'</p><pre style="background:'.
			' #ddd; border: 1px solid #aaa; padding: 15px;">'.
			$exception->getTraceAsString().'</pre>';
		$this->send_response(new Response($msg));
	}
	
	// This does nothing for now; just a hook for future features
	private static function clean_submitted(array $array)
	{
		return $array;
	}
	
	private function render_page_call($controller, $view, $args)
	{
		// Look for controller in user's directory
		$controller_dir = ConfigLoader::get_simple_value('general',
			'controllers_folder');
		$user_file = 'app/'.$controller_dir.'/'.$controller.'.php';
		if (file_exists(get_include_path().'/'.$user_file))
		{
			return $this->load_specific_view($user_file, $controller, $view,
				$args);
		}
		
		// Now try contrib
		;
		
		// Now try lib controllers
		$lib_file = 'lib/controllers/generic/'.$controller.'.php';
		if (file_exists(get_include_path().'/'.$lib_file))
		{
			return $this->load_specific_view($lib_file, $controller, $view,
				$args);
		}
		
		throw new Http404Exception('No file matching controller and view was'.
			' found to match.');
	}
	
	private function load_specific_view($file, $controller, $view, $args)
	{
		require_once($file);
		if (class_exists($controller)
		 && in_array($view, get_class_methods($controller)))
		{
			$params = $args?:array();
			$response = call_user_func_array(array($controller, $view), 
				array($this->request, &$params));
			if ($response instanceof Exception)
			{
				// Exceptions should have been thrown, but accept them as a
				// response instead if given that way
				throw $response;
			}
			elseif ($response instanceof Response)
			{
				return $response;
			}
			elseif (is_null($response))
			{
				// Try guessing the filename of the template, and rendering
				// that
				$t = new Template($controller.'/'.$view.'.php',
					new TemplateContext($params));
				return $t->render();
			}
			throw new HttpInvalidResponseException('View function did not '. 
				'return Response instance or throw exception');
		}
		else
		{
			throw new HttpMissingViewException('View does not exist in '.
				'controller');
		}
	}
}

?>